#ifndef VECTOR_GPU_H
#define VECTOR_GPU_H

#include "cuda_defs.h"
#include "dataType.h"

struct __align__(16) VectorGPU {
	__host__ __device__ DATA_TYPE& operator[] (int x) { return elements_[x]; }
	__host__ __device__ const DATA_TYPE& operator[] (int x) const { return elements_[x]; }

	__host__ __device__ DATA_TYPE getSquaredNorm() const;
	__host__ __device__ DATA_TYPE normalize();
	__host__ __device__ VectorGPU operator+(const VectorGPU& rhs) const;
	__host__ __device__ VectorGPU operator-(const VectorGPU& rhs) const;

	DATA_TYPE elements_[DIMENSIONS];
};

__host__ __device__ DATA_TYPE operator*(const VectorGPU& lhs, const VectorGPU& rhs);
__host__ __device__ VectorGPU operator*(DATA_TYPE lhs, const VectorGPU& rhs);

inline __host__ __device__ DATA_TYPE VectorGPU::getSquaredNorm() const
{
	DATA_TYPE sqNorm = 0;
	for (unsigned int i = 0; i < DIMENSIONS; ++i)
	{
		sqNorm += elements_[i] * elements_[i];
	}
	return sqNorm;
}

inline __host__ __device__ DATA_TYPE VectorGPU::normalize()
{
	DATA_TYPE norm = sqrt(getSquaredNorm());
	DATA_TYPE invNorm = (DATA_TYPE)1 / norm;
	if (norm > 0)
	{
		for (unsigned int i = 0; i < DIMENSIONS; ++i)
		{
			elements_[i] *= invNorm;
		}
	}

	return norm;
}

inline __host__ __device__ VectorGPU VectorGPU::operator+(const VectorGPU& rhs) const
{
	VectorGPU ret;
	for (unsigned int i = 0; i < DIMENSIONS; ++i)
	{
		ret.elements_[i] = elements_[i] + rhs.elements_[i];
	}
	return ret;
}

inline __host__ __device__ VectorGPU VectorGPU::operator-(const VectorGPU& rhs) const
{
	VectorGPU ret;
	for (unsigned int i = 0; i < DIMENSIONS; ++i)
	{
		ret.elements_[i] = elements_[i] - rhs.elements_[i];
	}
	return ret;
}

inline __host__ __device__ DATA_TYPE operator*(const VectorGPU& lhs, const VectorGPU& rhs)
{
	DATA_TYPE dot = 0;
	for (unsigned int i = 0; i < DIMENSIONS; ++i)
	{
		dot += lhs[i] * rhs[i];
	}
	return dot;
}

inline __host__ __device__ VectorGPU operator*(DATA_TYPE lhs, const VectorGPU& rhs)
{
	VectorGPU ret;
	for (unsigned int i = 0; i < DIMENSIONS; ++i)
	{
		ret[i] = lhs * rhs[i];
	}
	return ret;
}

#endif